#include "Definitions.h"
#include "SenetGame.h"
#include "SenetRandomPlayer.h"
#include "ExpectimaxPlayer.h"
#include "_061008926_026664482.h"
#include "GameTimer.h"
#include "Star1pPlayer.h"

#include <iostream>
#include <cstdlib>
#include <cerrno>
#ifndef WIN32
#include <time.h>
#include <sys/time.h>
#endif

using namespace std;

typedef enum {PLAYER_RANDOM , PLAYER_EXPECTIMAX , PLAYER_STAR1} PlayerType;

// Game parameters defaults
const int default_size_squares(30);					// The number of board squares
const int default_num_pieces(5);					// The number of pieces of each player
//	at the beginning of the game
const int default_num_extra_safe(0);				// The number of extra safe squares	
const int default_num_extra_rebirth(0);				// The number of extra rebirth squares
const int default_num_sticks(6);					// The number of sticks
const int default_max_moves(100);					// The number of allowed moves per player
const unsigned int default_chance_seed(0);			// The random generator's seed
const double default_time_per_move(20);				// Time per player's turn (in seconds)
const double default_time_for_constructing(1);		// Time for player's constructor
const double default_time_for_initialization(10);	// Time for player's initGame
const PlayerType default_p1(PLAYER_RANDOM);
const PlayerType default_p2(PLAYER_RANDOM);

// Globals for replacing default values with command line parameters
int size_squares(default_size_squares);
int num_pieces(default_num_pieces);
int num_extra_safe(default_num_extra_safe);
int num_extra_rebirth(default_num_extra_rebirth);
int num_sticks(default_num_sticks);
int chance_seed(default_chance_seed);
int max_moves(default_max_moves);
double time_per_move(default_time_per_move);
PlayerType p1_type(default_p1);
PlayerType p2_type(default_p2);
float p1_heuristicParams[7];
float p2_heuristicParams[7];


// Parse command line
// -squares			Board squares
// -pieces			Initial number of pieces per player
// -extra_safe		Number of extra safe squares
// -extra_rebirth	Number of extra rebirth squares
// -sticks			Number of sticks
// -seed			Random generator seed
// -moves			Number of maximum moves per player
//					(multiple moves due to sticks outcome are not counted)
// -time			Time per game move
bool ProcessCommandLine(int argc, char **argv);

// Output command line parameters' description
void PrintHelpMessage(char **argv);

Player* buildPlayer(GameTimer& initTimer,
					Definitions::PlayerColor color,
					GameTimer::TimeParams& timeParams,
					int maxMoves,
					bool* playerBadInit,
					PlayerType playerType,
					float* playerHeuristicParams)
{
	//TODO: remove 
// 	std::cout<<"build player heuristics are: ";
// 	for(int i=0; i< 7; ++i)
// 	{
// 		std::cout << playerHeuristicParams[i] << " ";		
// 	}
// 	std::cout << std::endl;
	
		
	initTimer.startMoveTimer();
	Player *p = NULL;
	switch(playerType)
	{
		case PLAYER_RANDOM: p = new SenetRandomPlayer(color, timeParams, maxMoves);
							break;
		case PLAYER_EXPECTIMAX: 
			{
				p = new ExpectimaxPlayer(color, timeParams, maxMoves);
//				((ExpectimaxPlayer*)p)->initHeurisitc(playerHeuristicParams);
				break;
			}
		case PLAYER_STAR1: p = new Star1pPlayer(color, timeParams, maxMoves);
							   //ExpectimaxPlayer(color, timeParams, maxMoves);
							break;
	}
	if(initTimer.isMoveTimePassed(GameTimer::USE_OVERHEAD))
	{
		*playerBadInit = true;
	}
	return p;
}

int main(int argc, char **argv)
{
 	char* argsTemp[] = {
		"AI_HW2",
		"-squares", "30",
        "-pieces", "5",
        "-extra_safe", "0",
        "-extra_rebirth", "0",
        "-sticks", "6",
        "-seed", "3",
        "-moves", "100",
        "-time", "1",
        "-p1_type", "2",
        "-p2_type", "0",
        "-h1" , "1", //h1a
        "-h2" , "2", //h2a
        "-h3" , "3", //h3a
        "-h4" , "4", //h4a
        "-h5" , "5", //h5a
        "-h6" , "6", //h6a
        "-h7" , "7", //h7a
        "-g1" , "11",//h1b
        "-g2" , "12",//h2b
        "-g3" , "13",//h3b
        "-g4" , "14",//h4b
        "-g5" , "15",//h5b
        "-g6" , "16",//h6b
        "-g7" , "17",//h7b
 	};
	if (argc == 1 /* no args */)
	{
		std::cout << "Using internal game parameters!";
		// getchar();
		argv = argsTemp;
 		argc = 49;
	}

	// Read game parameters from command line, if any
	// Game uses defaults for any command line parameter not specified
	if(!ProcessCommandLine(argc, argv))
	{
		PrintHelpMessage(argv);
		return 1;
	};

	srand(time(NULL) );

	// Game/move parameters
	GameTimer::TimeParams tp;
	tp.timePerGame_limit = false;
	tp.timePerMove_limit = true;
	std::cout << "time per move: " << time_per_move << std::endl;
	tp.timePerMove = time_per_move;// / 2.0; // TODO: remove / 2.0

	// Players construction
	// (initialization with game parameters is done as the first task
	// of SenetGame::playGame)
	bool Player1_bad_init(false), Player2_bad_init(false);
	// Time limits for construction, using timer's move limit
	GameTimer::TimeParams init_tp;
	init_tp.timePerGame_limit = false;
	init_tp.timePerMove_limit = true;
	init_tp.timePerMove = default_time_for_constructing;
	GameTimer init_timer(init_tp);

	// Constructing first player
	// It is white and will play first
	Player *p1 = buildPlayer(
		init_timer,	Definitions::WHITE,	tp,
		max_moves, &Player1_bad_init, p1_type,
		p1_heuristicParams);

	// Constructing second player
	// It is black and will play second
	Player *p2 = buildPlayer(
		init_timer,	Definitions::BLACK,	tp,
		max_moves, &Player2_bad_init, p2_type,
		p2_heuristicParams);

	// Check whether any player used too much time during construction
	if((Player1_bad_init || Player2_bad_init))
	{
		vector<string> playersNames;
		playersNames.push_back(p1->getName());
		playersNames.push_back(p2->getName());

		// Compute game results (premature termination - technical lose)
		Game::GameRes gameRes;
		gameRes.players_result.resize(2);

		if(Player1_bad_init)
			// Player 1 timeout
			gameRes.players_result[0] = Game::GameRes::TIMEOUT;
		else
			// Player 1 OK, player 2 timeout ==> Player 1 wins
			gameRes.players_result[0] = Game::GameRes::WIN;

		if(Player2_bad_init)
			// Player 2 timeout
			gameRes.players_result[1] = Game::GameRes::TIMEOUT;
		else
			// Player 2 OK, player 1 timeout ==> Player 2 wins
			gameRes.players_result[1] = Game::GameRes::WIN;

		delete p1;
		delete p2;

		// Output game result to the console
		cout << Game::ResultToString(gameRes, playersNames) << endl;
		// Exit program
		return 0;
	}

	// Players' construction went fine, start a game
	vector<Player*> players;
	players.push_back(p1);
	players.push_back(p2);

	// Initialize game
	SenetGame cng(players, default_time_for_initialization,
		tp, size_squares, num_pieces, num_extra_safe, num_extra_rebirth,
		num_sticks, chance_seed, max_moves);

	// Play game
	cng.playGame();

	// Output game result to the console
	cout << cng.ResultToString() << endl;

	//p1 result
	int p1resInt = (int)cng.getPlayerResult(0);
	int p2resInt = (int)cng.getPlayerResult(1);

	cout << std::endl;
	cout << "MARKER_MARKER_MARKER_MARKER:" << std::endl;
	cout << p1resInt << ":" << p2resInt << ":" << cng.getTurnsPlayed() << ":" << endl;

	// return int that represents the game results
	// since result < 10 we can return p1resInt + 10*p2resInt
	return p1resInt + (10*p2resInt) + (100* cng.getTurnsPlayed());
}

bool ProcessCommandLine(int argc, char **argv)
{
	bool errorEncountered(false);

	for(int i(1); (i < argc) && !errorEncountered; ++i)
	{
		if(string(argv[i]) == "-h")
		{
			errorEncountered = true;
		}
		else if(string(argv[i]) == "-squares")
		{
			errno = 0;
			size_squares = atoi(argv[++i]);
			if(size_squares < 1)
				errorEncountered = true;
		}
		else if(string(argv[i]) == "-pieces")
		{
			errno = 0;
			num_pieces = atoi(argv[++i]);
			if((num_pieces < 1) || (num_pieces > size_squares/2))
				errorEncountered = true;
		}
		else if(string(argv[i]) == "-extra_safe")
		{
			errno = 0;
			num_extra_safe = atoi(argv[++i]);
			if(num_extra_safe < 0)
				errorEncountered = true;
		}
		else if(string(argv[i]) == "-extra_rebirth")
		{
			errno = 0;
			num_extra_rebirth = atoi(argv[++i]);
			if(num_extra_rebirth < 0)
				errorEncountered = true;
		}
		else if(string(argv[i]) == "-sticks")
		{
			errno = 0;
			num_sticks = atoi(argv[++i]);
			if(num_sticks < 1)
				errorEncountered = true;
		}
		else if(string(argv[i]) == "-seed")
		{
			errno = 0;
			chance_seed = (unsigned int)atoi(argv[++i]);
			// no error encountered - everything is a legitimate seed
		}
		else if (string(argv[i]) == "-moves")
		{
			errno = 0;
			max_moves = atoi(argv[++i]);
			if(max_moves < 0)
				errorEncountered = true;
		}
		else if(string(argv[i]) == "-time")
		{
			errno = 0;
			time_per_move = atof(argv[++i]);
			if(time_per_move <= 0)
				errorEncountered = true;
		}
		else if (string(argv[i]) == "-p1_type")
		{
			errno = 0;
			p1_type = (PlayerType)atoi(argv[++i]);
			if(p1_type < 0 || p1_type > 3)
				errorEncountered = true;
		}
		else if (string(argv[i]) == "-p2_type")
		{
			errno = 0;
			p2_type = (PlayerType)atoi(argv[++i]);
			if(p2_type < 0 || p2_type > 3)
				errorEncountered = true;
		}
		else if(string(argv[i]) == "-h1")
		{
			errno = 0;
			p1_heuristicParams[(i-21)/2] = strtod(argv[++i], 0);
		}
		else if(string(argv[i]) == "-h2")
		{
			errno = 0;
			p1_heuristicParams[(i-21)/2] = strtod(argv[++i], 0);
		}
		else if(string(argv[i]) == "-h3")
		{
			errno = 0;
			p1_heuristicParams[(i-21)/2] = strtod(argv[++i], 0);
		}
		else if(string(argv[i]) == "-h4")
		{
			errno = 0;
			p1_heuristicParams[(i-21)/2] = strtod(argv[++i], 0);
		}
		else if(string(argv[i]) == "-h5")
		{
			errno = 0;
			p1_heuristicParams[(i-21)/2] = strtod(argv[++i], 0);
		}
		else if(string(argv[i]) == "-h6")
		{
			errno = 0;
			p1_heuristicParams[(i-21)/2] = strtod(argv[++i], 0);
		}
		else if(string(argv[i]) == "-h7")
		{
			errno = 0;
			p1_heuristicParams[(i-21)/2] = strtod(argv[++i], 0);
		}
		else if(string(argv[i]) == "-g1")
		{
			errno = 0;
			p2_heuristicParams[(i-35)/2] = strtod(argv[++i], 0);
		}
		else if(string(argv[i]) == "-g2")
		{
			errno = 0;
			p2_heuristicParams[(i-35)/2] = strtod(argv[++i], 0);
		}else if(string(argv[i]) == "-g3")
		{
			errno = 0;
			p2_heuristicParams[(i-35)/2] = strtod(argv[++i], 0);
		}
		else if(string(argv[i]) == "-g4")
		{
			errno = 0;
			p2_heuristicParams[(i-35)/2] = strtod(argv[++i], 0);
		}
		else if(string(argv[i]) == "-g5")
		{
			errno = 0;
			p2_heuristicParams[(i-35)/2] = strtod(argv[++i], 0);
		}
		else if(string(argv[i]) == "-g6")
		{
			errno = 0;
			p2_heuristicParams[(i-35)/2] = strtod(argv[++i], 0);
		}
		else if(string(argv[i]) == "-g7")
		{
			errno = 0;
			p2_heuristicParams[(i-35)/2] = strtod(argv[++i], 0);
		}
		

		else
		{
			errorEncountered = true;
		};
		if(errno != 0)
			errorEncountered = true;
	};

	return !errorEncountered;
}

void PrintHelpMessage(char **argv)
{
	cout << "Arguments for " << argv[0] << " :" << endl;
	cout
		<< "\t -squares <board squares> (default: " << default_size_squares << ")" << endl
		<< "\t -pieces <number of pieces> (default: " << default_num_pieces << ")" << endl
		<< "\t -extra_safe <number of squares> (default: " << default_num_extra_safe << ")" << endl
		<< "\t -extra_rebirth <number of squares> (default: " << default_num_extra_rebirth << ")" << endl
		<< "\t -sticks <number of sticks> (default: " << default_num_sticks << ")" << endl
		<< "\t -seed <chance seed> (default: " << default_chance_seed << ")" << endl
		<< "\t -moves <maximal number of moves per player> (default: " << default_max_moves << ")" << endl
		<< "\t -time <time limit per move, in seconds> (default: " << default_time_per_move << ")" << endl
		;
}
